package org.mxp.wobsocket;

import lombok.extern.slf4j.Slf4j;
import org.mxp.constant.MessageConstant;
import org.mxp.utils.ChatMessageUtil;
import org.mxp.utils.TableMessageUtil;
import org.mxp.utils.TextTableUtil;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @ServerEndpoint 通过这个 spring boot 就可以知道你暴露出去的 websockst 应用的路径，有点类似我们经常用的@RequestMapping。比如你的启动端口是8080，而这个注解的值是ws，那我们就可以通过 ws://127.0.0.1:8080/websocket 来连接你的应用
 * @OnOpen 当 websocket 建立连接成功后会触发这个注解修饰的方法，注意它有一个 Session 参数
 * @OnClose 当 websocket 建立的连接断开后会触发这个注解修饰的方法，注意它有一个 Session 参数
 * @OnMessage 当客户端发送消息到服务端时，会触发这个注解修改的方法，它有一个 String 入参表明客户端传入的值
 * @OnError 当 websocket 建立连接时出现异常会触发这个注解修饰的方法，注意它有一个 Session 参数
 */
@ServerEndpoint("/websocket/{id}")
@Component
@Slf4j
public class WebSocketServer {
    /**
     * concurrent包的线程安全Set，用来存放每个客户端对应的WebSocketController对象。
     */
    private static ConcurrentHashMap<String, WebSocketServer> webSocketMap = new ConcurrentHashMap<>();
    private Session session;
    private String id;

    private static ApplicationContext applicationContext;
    public static void setApplicationContext(ApplicationContext applicationContext) {
        WebSocketServer.applicationContext = applicationContext;
    }

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("id") String id) {
        this.session = session;
        this.id = id;
        session.getUserProperties().put("id", id);
        // 如果已经包含该用户id，则移除后重新加入
        if (webSocketMap.containsKey(id)) {
            webSocketMap.remove(id);
            webSocketMap.put(id, this);
        } else {
            // 否则直接加入
            webSocketMap.put(id, this);
        }
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(Session session) {
        String id = (String) session.getUserProperties().get("id");
        if (webSocketMap.containsKey(id)) {
            webSocketMap.remove(id);
            ChatMessageUtil chatMessageUtil = applicationContext.getBean(ChatMessageUtil.class);
            chatMessageUtil.removeSprayUser(id);
            log.info(">>> 用户：{}已关闭连接", id);
        } else {
            log.info(">>> 连接已关闭...");
        }
    }

    /**
     * 收到客户端消息后调用的方法
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(Session session, String message){
        // 接收传过来的消息
        log.info("收到的消息：{}", message);
        if (StringUtils.isEmpty(message)) {
            return;
        }
        TableMessageUtil tableMessageUtil = applicationContext.getBean(TableMessageUtil.class);
        ChatMessageUtil chatMessageUtil = applicationContext.getBean(ChatMessageUtil.class);
        if (message.startsWith(MessageConstant.TABLE_MSG_PREFIX)) {
            tableMessageUtil.addCell(message.substring(MessageConstant.TABLE_MSG_PREFIX.length()));
        } else if (message.startsWith(MessageConstant.CHAT_MSG_PREFIX)) {
            chatMessageUtil.addChat(message.substring(MessageConstant.CHAT_MSG_PREFIX.length()));
            String userId = (String) session.getUserProperties().put("id", id);
            if (chatMessageUtil.getSpray(userId)) {
                String str = TextTableUtil.getTableData(chatMessageUtil.getMsg(message.substring(MessageConstant.CHAT_MSG_PREFIX.length())));
                tableMessageUtil.addListCell(str);
                String tableData = MessageConstant.TABLE_MSG_PREFIX + str;
                // 发送消息
                sendAllUserMessage(session, tableData);
            }
        }
        // 发送消息
        sendOtherAllUserMessage(session, message);
    }

    /**
     * 实现服务器主动推送
     */
    public void sendMessage(String message) {
        try {
            System.out.println(webSocketMap.keySet());
            this.session.getBasicRemote().sendText(message);
        } catch (Exception e) {
            log.error(">>> WebSocket消息发送出现错误: {}", e.getMessage(), e);
        }
    }

    /**
     * 给其他所有用户发送消息
     */
    public void sendOtherAllUserMessage(Session session, String message) {
        String id = (String) session.getUserProperties().get("id");
        for (String tempId : webSocketMap.keySet()) {
            if (!tempId.equals(id)) {
                WebSocketServer webSocketServer = webSocketMap.get(tempId);
                webSocketServer.sendMessage(message);
            }
        }
    }

    /**
     * 给其他所有用户发送消息
     */
    public void sendAllUserMessage(Session session, String message) {
        for (String tempId : webSocketMap.keySet()) {
            WebSocketServer webSocketServer = webSocketMap.get(tempId);
            webSocketServer.sendMessage(message);
        }
    }

    /**
     * 清空
     */
    public static void clear() {
        for (String tempId : webSocketMap.keySet()) {
            WebSocketServer webSocketServer = webSocketMap.get(tempId);
            webSocketServer.sendMessage(MessageConstant.TABLE_MSG_CLEAR);
        }
    }

}